<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<script type="text/javascript">
			1.
			/*
			 let命令，用来声明变量。它的用法类似于var，但是所
			 声明的变量，只在let命令所在的代码块内有效
			 for循环的计数器，就很合适使用let命令。
			 * */
			{
			  let a = 10;
			  var b = 1;
			}
			
			a // ReferenceError: a is not defined.
			b // 1
			
			for (let i = 0; i < 10; i++) {
			  // ...
			}
			
			console.log(i);
			// ReferenceError: i is not defined
			
			2.
			var a = [];
			for (var i = 0; i < 10; i++) {
			  a[i] = function () {
			    console.log(i);
			  };
			}
			a[6](); // 10
			console.log(i)//10
			
			/*
			 上面代码中，变量i是var命令声明的，在全局范围内都有效，
			 所以全局只有一个变量i。每一次循环，变量i的值都会发生改变，.
			 而循环内被赋给数组a的函数内部的console.log(i)，里面的i指
			 向的就是全局的i。也就是说，所有数组a的成员里面的i，
			 指向的都是同一个i，导致运行时输出的是最后一轮的i的值，也就是 10。
			如果使用let，声明的变量仅在块级作用域内有效，最后输出的是 6
			 * */
			
			var a = [];
			for (let i = 0; i < 10; i++) {
			  a[i] = function () {
			    console.log(i);
			  };
			}
			a[6](); // 6
			
			/*
			 上面代码中，变量i是let声明的，当前的i只在本轮循环有效，
			 所以每一次循环的i其实都是一个新的变量，所以最后输出的是6。
			 你可能会问，如果每一轮循环的变量i都是重新声明的，
			 那它怎么知道上一轮循环的值，从而计算出本轮循环的值？
			 这是因为 JavaScript 引擎内部会记住上一轮循环的值，.
			 初始化本轮的变量i时，就在上一轮循环的基础上进行计算。
			 * */
			
			3.
			/*
			 var命令会发生“变量提升”现象，即变量可以在声明之前使用，
			 值为undefined。这种现象多多少少是有些奇怪的，按照一般的逻辑，
			 变量应该在声明语句之后才可以使用。
			为了纠正这种现象，let命令改变了语法行为，它所声明的变量
			一定要在声明后使用，否则报错
			 * */
			// var 的情况
			console.log(foo); // 输出undefined
			var foo = 2;
			
			// let 的情况
			console.log(bar); // 报错ReferenceError
			let bar = 2;
			
			
			/*
			 * 只要块级作用域内存在let命令，它所声明的变量就
			 * “绑定”（binding）这个区域，不再受外部的影响
			 使用let命令声明变量之前，该变量都是不可用的。
			 这在语法上，称为“暂时性死区”（temporal dead zone，简称 TDZ）。
			 * */
			if (true) {
			  // TDZ开始
			  tmp = 'abc'; // ReferenceError
			  console.log(tmp); // ReferenceError
			
			  let tmp; // TDZ结束
			  console.log(tmp); // undefined
			
			  tmp = 123;
			  console.log(tmp); // 123
			  
			  /*
			   如果一个变量根本没有被声明,或者在后面使用了var进行声明
			   则不会报错
			   * */
			  console.log(name)
			  var name='aa'
			}
			
			
			4.
			/*
			 let不允许在相同作用域内，重复声明同一个变量。
			 * */
			// 报错
			function func() {
			  let a = 10;
			  var a = 1;
			}
			
			// 报错
			function func() {
			  let a = 10;
			  let a = 1;
			}
			
			
			5.
			/*
			 ES5 只有全局作用域和函数作用域，没有块级作用域，
			 这带来很多不合理的场景。
			 * */
			 //第一种场景，内层变量可能会覆盖外层变量,
			var tmp = new Date();
			function f() {
			  console.log(tmp);
			  if (false) {
			    var tmp = 'hello world';
			  }
			}
			f(); // undefined
			/*
			 上面代码的原意是，if代码块的外部使用外层的tmp变量，
			 内部使用内层的tmp变量。但是，函数f执行后，输出结果
			 为undefined，原因在于变量提升，导致内层的tmp变量
			 覆盖了外层的tmp变量。
			 * */
			//第二种场景，用来计数的循环变量泄露为全局变量。
			var s = 'hello';
			for (var i = 0; i < s.length; i++) {
			  console.log(s[i]);
			}
			console.log(i); // 5
			/*
			 变量i只用来控制循环，但是循环结束后，它并没有消失，
			 泄露成了全局变量
			 * */
			
			/*let实际上为 JavaScript 新增了块级作用域。*/
			function f1() {
			  let n = 5;
			  if (true) {
			    let n = 10;
			  }
			  console.log(n); // 5
			}
			/*
			 上面的函数有两个代码块，都声明了变量n，运行后输出 5。
			 这表示外层代码块不受内层代码块的影响。如果两次都使用var
			 定义变量n，最后输出的值才是 10。
			 * */
		</script>
	</body>
</html>
